<template>
  <div @mousedown.stop>
    <!--    @input="input"-->
    <!--
      compositionstart 与 compositionend  目的是让输入汉字的时候没有输入完成不触发input事件 （在打拼音的时候不让触发input事件
     -->
    <div class="text-area" ref="textarea" :contenteditable="isEdit"
         v-html="value"
         @keyup="keyup" @keydown="keydown"
         @input="input" @blur="blur"
         @compositionstart="inputLock = true" @compositionend="compositionend"
         @click="clickHtml" @paste.prevent="paste"
    />

    <div class="container" v-show="menuShow" v-clickoutside="clickoutside" :style="style">
      <button class="user-item" :class="{ 'user-item-active': userActive === idx }"
              v-for="(item, idx) in localUserList" :key="idx" @mousedown="chooseUser(idx, $event)"
      >
        <div>{{ item.userName }}</div>
      </button>
    </div>
  </div>
</template>

<script>

import { offset } from './caret-pos.js'
import { clickoutside } from '../utils.js'

import calcUserList from './calcUserList.js'

const execEditorCommand = (name, args = null) => {
  document.execCommand(name, false, args)
}
export default {
  directives: { clickoutside },
  props: {
    value: { default: '' }, // 富文本字符串
    isEdit: { default: true },// 是否可编辑
    // autoFocus: { default: false },  // 是否自动聚焦， --应该是没用了
    userList: { default: () => [] }, // 所有候选人列表, 对其筛选后赋值给 localUserList
    isAit: { type: Boolean, default: false } // 是否有艾特人的功能
  },
  watch: {
    userList: {
      handler(nv) {
        this.localUserList = nv
      },
      immediate: true
    },
    menuShow() {
      this.userActive = 0
    }
  },
  data: () => ({
    style: {},
    menuShow: false,
    userActive: 0,
    hintWordCopy: '',
    localUserList: [], // 候选人列表  --显示在页面上的
    inputLock: false  // 中文输入法下, 控制是否触发input事件
  }),
  mounted() {
    // if (this.autoFocus) this.$refs.textarea.focus()
  },
  methods: {
    // 这个方法目前只在外部进行调用
    async focus() {
      const textArea = this.$refs.textarea
      textArea.focus()

      // 点击空白让光标在最后
      const sel = window.getSelection()
      sel.collapse(textArea.lastChild, textArea.lastChild.length)
    },
    //设置候选人列表出现的位置
    setMenuPosition() {
      const ele = this.$refs.textarea
      const off = offset(ele)   // 获取光标位置
      this.style = { left: `${off.left}px`, top: `${off.top + 30}px` }
    },
    // 点击外部关闭候选人
    clickoutside(targetDom) {
      if (this.$refs.textarea === targetDom) return // 点击输入框的时候什么也不做 在click事件内处理 `menuShow` 显隐
      this.menuShow = false
    },
    // 如果是鼠标点击 会有事件对象, 阻止默认事件, 使输入框不失去焦点
    chooseUser(idx, evt) {
      evt && evt.preventDefault()

      const user = this.localUserList[idx]
      console.log(this.hintWordCopy.length)
      const num = this.hintWordCopy.length
      for(let i=0;i<num;i++) {
        execEditorCommand('delete', null)   // 删除 @
      }

      let spanNode1 = document.createElement('span')
      let spanNode2 = document.createElement('span')
      spanNode1.className = 'user-tag'
      spanNode1.contentEditable = 'false'
      spanNode1.innerHTML = '@' + user.userName
      spanNode1.setAttribute('userid', user.userId)

      spanNode2.innerHTML = '&nbsp'

      let selection = getSelection()
      let range = selection.getRangeAt(0)

      // 将生成内容打包放在 Fragment 中，并获取生成内容的最后一个节点，也就是空格
      let frag = document.createDocumentFragment(),
        node,
        lastNode
      frag.appendChild(spanNode1)
      console.log(spanNode2.firstChild)
      while ((node = spanNode2.firstChild)) {
        lastNode = frag.appendChild(node)
      }

      range.insertNode(frag)  // 插入
      selection.collapse(lastNode, 1) // 控制光标
      this.menuShow = false
    },
    // 粘贴事件
    paste(evt) {
      const text = evt.clipboardData.getData('text')
      const textNode = document.createTextNode(text)

      const sel = window.getSelection()
      const range = sel.getRangeAt(0)

      range.insertNode(textNode)
      sel.collapseToEnd()
    },
    input() {
      if (this.inputLock) return
      if(!this.isAit) return
      this.filterUserList()
    },
    // 汉字输入进输入框后手动触发input事件
    compositionend() {
      this.inputLock = false
      // 手动触发input事件
      this.$refs.textarea.dispatchEvent(new Event('input'))
    },
    // 失去焦点时计算人员，位置，长度 等
    async blur(evt) {
      await this.$nextTick()

      const { innerText, innerHTML } = evt.target

      const relateUserList = calcUserList(innerHTML) // 得到一个对象数组
      const content = { text: innerText, htmlText: innerHTML, relateUserList }

      // console.log(JSON.stringify(content, null, '    '))

      const jsonstr = JSON.stringify(content)

      this.$emit('input', innerHTML)  // 失去焦点
      this.$emit('blur', jsonstr)
    },

    // 点击的时候获取筛选人列表, -也就是点击到了@符号后边会出现候选人列表
    clickHtml(evt) {
      if(!this.isAit) return
      const { target } = evt
      if (target.nodeName === 'SPAN') {
        console.log(target.getAttribute('userid'))  // 可以获取到点击的人的 userid
      }
      this.filterUserList()
    },
    // 出现人列表的时候处理按上下箭头  --候选人出现的时候 按上下箭头选人, 回车提交
    handleUpDownArrow(evt) {
      if (!this.menuShow) return
      // 上箭头
      if (evt.key === 'ArrowUp') {
        if (this.userActive > 0) this.userActive--
      }
      // 下箭头
      if (evt.key === 'ArrowDown') {
        if (this.userActive < this.localUserList.length - 1) this.userActive++
      }
      // 回车
      if (evt.key === 'Enter') this.chooseUser(this.userActive)
      // 左上角的 esc
      if (evt.key === 'Escape') this.menuShow = false
    },
    // 根据关键词筛选用户列表
    filterUserList() {
      const selection = getSelection()
      const range = selection.getRangeAt(0)
      const text = range.endContainer.data
      const focusOffset = selection.focusOffset

      if(!text) return
      const targetText = text.substring(0, focusOffset)
      const qaIdx = targetText.lastIndexOf('@')
      // 如果找到了 `@`, 显示候选人列表
      if (qaIdx !== -1) {
        const hintWord = targetText.substring(qaIdx)
        console.log('hintWord', hintWord)
        this.hintWordCopy = hintWord

        if (!hintWord) return this.menuShow = false

        if (hintWord === '@') {  // 如果关键词是 @, 显示所有候选人
            this.localUserList = this.userList
        }
        else { // 如果是'@张'这样的, 根据'张'筛选候选人列表
          const resultUserList = this.userList.filter(x => x.userName.includes(hintWord.substring(1)))

          if (!resultUserList.length) return this.menuShow = false
          else this.localUserList = resultUserList
        }

        this.menuShow = true
        this.setMenuPosition()
      } else this.menuShow = false  // 没找到就隐藏候选人列表
    },

    // 键盘松开的时候,
    keyup(evt) {
      if(!this.isAit) return    // 如果没有艾特人的功能，什么也不做
      if (evt.key === '@') {
        this.setMenuPosition()
        this.menuShow = true

        this.localUserList = this.userList
      }
      // 处理上下箭头  --当候选人出现的时候 按上下箭头选人, 回车提交
      this.handleUpDownArrow(evt)

      // 处理左右箭头  --进行候选人的筛选
      if (evt.key === 'ArrowLeft' || evt.key === 'ArrowRight') this.filterUserList()
    },
    keydown(e) { // 按下这些键阻止默认事件
      ['Enter', 'ArrowUp', 'ArrowDown'].includes(e.key) && e.preventDefault()
    }
  }
}
</script>

<style scoped lang="scss">

.text-area {
  &:focus {
    border-color: #0a51c5;
  }
}

.container {
  width: 180px;
  max-height: 300px;
  background-color: #fff;
  position: fixed;
  z-index: 66;
  box-shadow: 0 0 5px 4px #eee;
  border-radius: 5px;
  padding: 10px 0;
  overflow-y: auto;

  .user-item {
    width: 100%;
    height: 40px;
    border: none;
    outline: none;
    cursor: pointer;
    background-color: #fff;
    transition: 0.1s;

    &:hover {
      background-color: #eee;
    }
  }

  .user-item-active {
    background-color: #eee;
  }
}


::v-deep .user-tag {
  color: #fff;
  background-color: #07f;
  padding: 0 5px;
  border-radius: 999em;
  display: inline-block;
  user-select: all;
}

</style>
